const myBind = require("./template.js")

function testBind() {
  console.log(
    "1. 基本this绑定:",
    (() => {
      const obj = { name: "Alice" }
      function getName() {
        return this.name
      }
      const boundGetName = myBind(getName, obj)
      return boundGetName() === "Alice"
    })()
  )

  console.log(
    "2. 预设参数:",
    (() => {
      function add(a, b, c) {
        return a + b + c
      }
      const addFive = myBind(add, null, 5)
      return addFive(2, 3) === 10
    })()
  )

  console.log(
    "3. 多个预设参数:",
    (() => {
      function greet(greeting, name, punctuation) {
        return `${greeting}, ${name}${punctuation}`
      }
      const sayHello = myBind(greet, null, "Hello")
      return sayHello("World", "!") === "Hello, World!"
    })()
  )

  console.log(
    "4. this上下文和参数结合:",
    (() => {
      const calculator = {
        multiplier: 10,
        multiply: function (a, b) {
          return (a + b) * this.multiplier
        }
      }
      const boundMultiply = myBind(calculator.multiply, calculator, 2)
      return boundMultiply(3) === 50 // (2+3)*10
    })()
  )

  console.log(
    "5. 箭头函数绑定:",
    (() => {
      const obj = { value: 42 }
      const arrowFn = () => this.value
      const boundArrow = myBind(arrowFn, obj)
      // 箭头函数的this无法绑定，应该返回undefined
      return boundArrow() === undefined
    })()
  )

  console.log(
    "6. 原始函数不变:",
    (() => {
      const obj1 = { name: "Original" }
      const obj2 = { name: "Bound" }
      function getName() {
        return this.name
      }

      const boundGetName = myBind(getName, obj2)
      const result1 = getName.call(obj1)
      const result2 = boundGetName()

      return result1 === "Original" && result2 === "Bound"
    })()
  )

  console.log(
    "7. 多次调用绑定函数:",
    (() => {
      let callCount = 0
      const obj = { value: 100 }
      function counter() {
        callCount++
        return this.value + callCount
      }

      const boundCounter = myBind(counter, obj)
      return boundCounter() === 101 && boundCounter() === 102
    })()
  )

  console.log(
    "8. 无参数函数:",
    (() => {
      const obj = { message: "Hello World" }
      function getMessage() {
        return this.message
      }
      const boundGetMessage = myBind(getMessage, obj)
      return boundGetMessage() === "Hello World"
    })()
  )

  console.log(
    "9. null/undefined作为context:",
    (() => {
      function getThis() {
        return this
      }
      const boundWithNull = myBind(getThis, null)
      const boundWithUndefined = myBind(getThis, undefined)

      // 在非严格模式下，null/undefined会被转换为全局对象
      return (
        typeof boundWithNull() !== "undefined" &&
        typeof boundWithUndefined() !== "undefined"
      )
    })()
  )

  console.log(
    "10. 复杂对象方法绑定:",
    (() => {
      const person = {
        firstName: "John",
        lastName: "Doe",
        getFullName: function (prefix = "", suffix = "") {
          return `${prefix}${this.firstName} ${this.lastName}${suffix}`
        }
      }

      const boundGetFullName = myBind(person.getFullName, person, "Mr. ")
      return boundGetFullName("Jr.") === "Mr. John DoeJr."
    })()
  )

  console.log(
    "11. 数组方法绑定:",
    (() => {
      const arr = [1, 2, 3, 4, 5]
      const boundSlice = myBind(Array.prototype.slice, arr, 1)
      const result = boundSlice(3)
      return JSON.stringify(result) === JSON.stringify([2, 3])
    })()
  )

  console.log(
    "12. 构造函数绑定:",
    (() => {
      function Person(name, age) {
        this.name = name
        this.age = age
      }

      const BoundPerson = myBind(Person, null, "Alice")
      try {
        const instance = new BoundPerson(25)
        return instance.name === "Alice" && instance.age === 25
      } catch (e) {
        // 如果绑定函数不能用作构造函数，这也是合理的
        return true
      }
    })()
  )

  console.log(
    "13. 函数原型方法:",
    (() => {
      function testFunc() {
        return "test"
      }
      testFunc.customProperty = "custom"

      const boundFunc = myBind(testFunc, null)
      return boundFunc() === "test"
    })()
  )

  console.log(
    "14. 嵌套绑定:",
    (() => {
      const obj1 = { value: 1 }
      const obj2 = { value: 2 }

      function getValue() {
        return this.value
      }

      const bound1 = myBind(getValue, obj1)
      const bound2 = myBind(bound1, obj2) // 再次绑定应该无效

      return bound1() === 1 && bound2() === 1
    })()
  )

  console.log(
    "15. 动态参数处理:",
    (() => {
      function sum(...numbers) {
        return numbers.reduce((a, b) => a + b, 0)
      }

      const addToTen = myBind(sum, null, 10)
      return addToTen(5, 3, 2) === 20
    })()
  )

  console.log(
    "16. 访问器属性绑定:",
    (() => {
      const obj = {
        _value: 42,
        get value() {
          return this._value
        },
        set value(val) {
          this._value = val
        }
      }

      const boundGetter = myBind(
        Object.getOwnPropertyDescriptor(obj, "value").get,
        obj
      )
      return boundGetter() === 42
    })()
  )

  console.log(
    "17. 异步函数绑定:",
    (() => {
      const obj = { value: "async" }
      async function getAsyncValue() {
        return this.value
      }

      const boundAsync = myBind(getAsyncValue, obj)
      // 检查返回的是否是Promise
      return boundAsync() instanceof Promise
    })()
  )

  console.log(
    "18. 异常处理:",
    (() => {
      function throwError() {
        throw new Error("Test error")
      }

      const boundThrow = myBind(throwError, null)
      try {
        boundThrow()
        return false
      } catch (e) {
        return e.message === "Test error"
      }
    })()
  )

  console.log(
    "19. 返回值类型保持:",
    (() => {
      const obj = { data: [1, 2, 3] }
      function getData() {
        return this.data
      }

      const boundGetData = myBind(getData, obj)
      const result = boundGetData()

      return Array.isArray(result) && result === obj.data
    })()
  )

  console.log(
    "20. 与原生bind对比:",
    (() => {
      const obj = { value: "test" }
      function getValue(prefix, suffix) {
        return prefix + this.value + suffix
      }

      const myBoundFunc = myBind(getValue, obj, "[")
      const nativeBoundFunc = getValue.bind(obj, "[")

      return myBoundFunc("]") === nativeBoundFunc("]")
    })()
  )
}

testBind()
